using System;
using System.Collections.Generic;
using Servlets.Api;
using Servlets.Exceptions;
using System.IO;
using Servlets.Http;
using Servlets.Utils;

namespace Servlets.Server
{
	class RequestDispatcher : IRequestDispatcher {
		private Mapping mapping;
		private Queue<string> requestChain = new Queue<string>();
		
		public RequestDispatcher(Mapping mapping) {
			this.mapping = mapping;
		}
		
		public void HandleRequest(IClient client) {
			Log.WriteLine("");
			Log.WriteLine("[INFO] Accepted Client");
			
			IRequest request = null;
			IResponse response = null;
			StreamReader reader = null;
			StreamWriter writer = null;
			try {				
				reader = client.StreamReader;
				writer = client.StreamWriter;
				
				response = new HttpResponse(writer);
				request = new HttpRequest(reader, response, this);
				Log.WriteLine("[INFO]\t Requested Url: " + request.Url);
				RunFilterChain(request, response);
				StartRequestChain(request, response);
				Log.WriteLine("[INFO]\t Request Finished Successfully");
			} catch (ServletException e) {
				HandleServletException(e, response);
			} catch (Exception e) {
				if(e.InnerException is ServletException) {
					HandleServletException(e.InnerException as ServletException, response);
				} else {
					HandleException(e, response);
				}
			} finally {
				if(response != null) {
					response.OutputStream.Close();
				}
				client.Dispose();
				
				Log.WriteLine("[INFO] End Request");
				Log.WriteLine("");
			}
		}
		
		private void HandleServletException(ServletException e, IResponse response) {
			if(response != null) {
				response.HttpStatus = e.StatusCode;// HttpStatus.STATUS_500_INTERNAL_SERVER_ERROR;
				HandleException(e, response);
			} else {
				Log.WriteLine (e);
			}
		}
		
		private void HandleException(Exception e, IResponse response) {
			Log.WriteLine (e);
			if(response == null) {
				return;
			}
			//TODO: Forward to a dynamic page
			response.OutputStream.Clear();
			string ex = e == null ? "{null}" : e.ToString().ToHtml();
			response.OutputStream.WriteLine("<html>");
			response.OutputStream.WriteLine(	"<body>");
			response.OutputStream.WriteLine(		"<h1>An exception was thrown</h1>");
			response.OutputStream.WriteLine(		"<p>" +ex +"</p>");
			if(e.InnerException != null) {
				response.OutputStream.WriteLine(	"<h3>Inner Exception</h3>");
				response.OutputStream.WriteLine(	"<p>" +e.InnerException +"</p>");
			}
			response.OutputStream.WriteLine(	"</body>");
			response.OutputStream.WriteLine("</html>");
		}
		
		private void RunFilterChain(IRequest request, IResponse response) {
			
		}
		
		private void StartRequestChain(IRequest request, IResponse response) {
			if(request.Url.EndsWith(".milk") && Server.Config.ProtectViews) {
				throw new NotFoundException(request.Url);
			}
			
			requestChain.Enqueue(request.Url);
			IRequest nextRequest = request;
			
			while(requestChain.Count > 0) {
				string url = requestChain.Dequeue();
				nextRequest = new HttpRequest(url, nextRequest);
				Forward(nextRequest, response);
			}
		}
		
		public void Forward(string newurl) {
			requestChain.Enqueue(newurl);
		}
		
		public void Include(string newurl, IRequest oldRequest, IResponse response) {
			HttpRequest newRequest = new HttpRequest(newurl, oldRequest);
			Forward(newRequest, response);
		}
		
		private void Forward(IRequest request, IResponse response) {
			ServletInvoker invoker = mapping.GetServletByUrl(request.Url, request.HttpMethod);
			invoker.Invoke(request, response);
		}
	}
	/*
	class RequestDispatch {
		public IRequest Request{get;private set;}
		public IResponse Response{get;private set;}
		
		public RequestDispatch(IRequest request, IResponse response) {
			this.Request = request;
			this.Response = response;
		}
	}
	*/
}
